#include "game_script.h"
#include "game_rms.h"
#include "game_application.h"
#include "application/game_script.h"

#include "script/scripthelper.h"
#include "script/file_list.h"
#include "lang/strman.h"

#include "vfs/archive.h"

#include "lang/lang.h"

#include "input/keyboard.h"
#include "console/console.h"

#include "gameflow/ui_popup.h"

#include "font/font_manager.h"
#include "gameflow/game_flow_manager.h"
#include "ui/ui_manager.h"
#include "audio/audio.h"
#include "bones/biped_anim.h"
///#include "web/server.h"

#include "math/ramp.h"

#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    
#include "util/timer.h"		    

#include <cassert>

using namespace script;
using namespace LuaPlus;

#ifdef _ENABLE_LUABIND
#include <luabind/luabind.hpp>
#include <luabind/operator.hpp>					// CHIT:  Need this for "luabind::self"
#include <luabind/copy_policy.hpp>
using namespace luabind;
#endif // _ENABLE_LUABIND

GameScript*	GameScript::Instance()
{
	return g_pApp->GetScript();
}

GameScript::GameScript()
{

}

GameScript::~GameScript()
{

}


static void CreateNewFont(const std::string& Name, const std::string& Typeface, int Height, int Width)
{
	FontManager::Instance().InitFont(Name,  Typeface, Height, Width);
}
static	std::string	s_CurrentLocale = "english";
static void	SetLocale(const std::string& Locale)
{
	s_CurrentLocale = Locale;
	ui::UIManager::Instance().SetLocalization(Locale);
	ui::UIManager::Instance().UpdateLocalization();
}

static void	SetFontBorder(const std::string& Name, bool Border)
{
	Font* pFont =	FontManager::Instance().GetFont(Name);
	if (pFont)
	{
		pFont->SetBorder(Border);
	} 
}

static void SetFontColorMask(const std::string& Name, float r, float g, float b, float a)
{
	Font* pFont =	FontManager::Instance().GetFont(Name);
	if (pFont)
	{
		pFont->SetColorMask(r, g, b, a);
	}
}

static void	ResizeExistingFont(const std::string& Name, const std::string& Face, int Width, int Height, int Weight)
{

#ifndef _PLATFORM_WIN32

//// TODO: will this entire 'set' of functions need to be rewritten for multi-platform?
//// TODO: what's a reasonable response in situations where you don't have TTF support (such as Wii)?
	static const int		DEFAULT_CHARSET = 0;
	static const int		HANGUL_CHARSET = 129;
	static const int		CHINESEBIG5_CHARSET = 136;
	static const int		GB2312_CHARSET = 134;
	static const int		SHIFTJIS_CHARSET = 128;

#endif

	Font* pFont =	FontManager::Instance().GetFont(Name);
	unsigned int	LineSpace = 0;
	unsigned long	CharSet = DEFAULT_CHARSET;
	if (strcmpi(s_CurrentLocale.c_str(), "korean")==0)		{ LineSpace = 2; CharSet = HANGUL_CHARSET;		}else
	if (strcmpi(s_CurrentLocale.c_str(), "chinese")==0)		{ LineSpace = 2; CharSet = CHINESEBIG5_CHARSET; }else
	if (strcmpi(s_CurrentLocale.c_str(), "taiwanese")==0)	{ LineSpace = 2; CharSet = GB2312_CHARSET;		}else
	if (strcmpi(s_CurrentLocale.c_str(), "japanese")==0)	{ LineSpace = 2; CharSet = SHIFTJIS_CHARSET;	}
	if (pFont)
	{

		pFont->Resize(Face, Width, Height, Weight, CharSet);
	} else
	{
		pFont = FontManager::Instance().InitFont(Name,  Face, Width, Height, false, Weight, CharSet);
	}
	
	pFont->SetLineSpacing(LineSpace);
}

void	GameScript::ResetFonts(const std::string& File, const std::string& Language)
{
	LuaPlus::LuaState*	pScript = ScriptHelper::CreateLuaState();
	if (!ScriptHelper::DoFile(*pScript, File))
	{
		_LOG(MSG_ERROR, "Unable to load font data " << File << "!");
		 LuaState::Destroy( pScript );
		return;
	} else
	{
		std::string	ActiveLanguage = Language;
		if (ActiveLanguage.empty())	ActiveLanguage	= "default";
		LuaPlus::LuaObject	Info	= pScript->GetGlobal("FONTS");
		if (!Info.IsTable())
		{
			_LOG(MSG_ERROR, "Unable to load font data " << File << ". No active FONTS object!");
			return;
		}
		LuaPlus::LuaObject	FontSet = ScriptHelper::GetObj(Info, ActiveLanguage);
		std::string	MapTo = ScriptHelper::GetTableString(FontSet, "MapTo");
		if (!MapTo.empty())
		{
			ActiveLanguage = MapTo;
			FontSet = ScriptHelper::GetObj(Info, ActiveLanguage);
		}	

		if (!FontSet.IsTable())
		{
			_LOG(MSG_ERROR, "Unable to load font data " << File << ". No active language " << ActiveLanguage << "!");
			return;
		}
		SetLocale(Language);
		FontManager::Instance().SetDefaultFont(NULL);
//		console::Console::Instance().SetFont(NULL);

		for (int i=0; i<FontSet.GetTableCount(); i++)
		{
			LuaPlus::LuaObject	Font = FontSet.GetByIndex(i+1);
			std::string		Name = ScriptHelper::GetTableString(Font, "Name");
			// kill the old one
			FontManager::Instance().RemoveElement(Name, true);

			std::string	Bitmap	= ScriptHelper::GetTableString(Font, "Bitmap");
			if (!Bitmap.empty())
			{
				FontManager::Instance().InitBitmapFont(Name, Bitmap);
			} else
			{
				std::string	Face			= ScriptHelper::GetTableString(Font, "Face");
				bool		Border			= ScriptHelper::GetTableBool(Font, "Border", false);
				int			Width			= ScriptHelper::GetTableInt(Font, "Width", 0);
				int			Height			= ScriptHelper::GetTableInt(Font, "Height", 20);
				int			Weight			= ScriptHelper::GetTableInt(Font, "Weight", 500);
				math::Vec4	ColorMask		= ScriptHelper::GetTableVec4(Font, "Color", math::Vec4(1,1,1,1));

				ResizeExistingFont(Name, Face, Width, Height, Weight);
				SetFontBorder(Name, Border);
				SetFontColorMask(Name, ColorMask.x, ColorMask.y, ColorMask.z, ColorMask.w);
			}
		}
		ui::UIManager::Instance().RefreshFonts();
	}
	LuaState::Destroy( pScript );

}

static std::string FormatCommaNumber(int Num) // ADRIAN: any better place to put this function?
{
	return lang::Lang::FormatCommaNumber(Num);
}

static void	ForceSave()
{
	GameRMS::Instance()->Save();
}

static void	FullScreenMode()
{
	g_pApp->GetHandler()->SwitchFullscreen(g_pApp->GetHandler()->GetWidth(),g_pApp->GetHandler()->GetHeight()); 
	GameRMS::Instance()->SetUseFullscreenEnabled(true);
}	

static void	WindowedMode()
{
	unsigned int Width, Height, Depth;
	g_pApp->GetHandler()->GetCurrentResolution(Width, Height, Depth);

	if (g_pApp->GetHandler()->GetWidth() >= Width ||
		g_pApp->GetHandler()->GetHeight() >= Height)
	{
	/// windowed mode won't work if it's too small.. so pop up a message instead
		gameflow::UIPopupManager::Instance().StartPrompt("RESOLUTION_TOO_SMALL", "RESOLUTION_TOO_SMALL_TXT", "OK_TXT", NULL);		
		return;
	}
	
	g_pApp->GetHandler()->SwitchWindowed(g_pApp->GetHandler()->GetWidth(),g_pApp->GetHandler()->GetHeight()); 
	GameRMS::Instance()->SetUseFullscreenEnabled(false);
}	

static bool IsFullscreenMode()
{
	return g_pApp->GetHandler()->IsFullscreen();
}

static void	HardQuitGame()
{
	ForceSave();
	if (IsFullscreenMode())
	{
		g_pApp->GetHandler()->SetIsQuitting();
		g_pApp->GetHandler()->SwitchWindowed(g_pApp->GetHandler()->GetWidth(),
										g_pApp->GetHandler()->GetHeight());

	}

	g_pApp->Quit();	//GetHandler()->DestroyWindow();
}	

static void	TakeScreenshot(const std::string& File, bool Minimize = false)
{
	GetGraphics()->TakeScreenShot(File, Minimize);
}
static void	ToggleFilter()
{
	GetGraphics()->SetFilter2D(!GetGraphics()->GetFilter2D());
}

static void	ReloadImages()
{
//	g_pApp->RefreshAssets();
}



std::string	GameScript::GetSaveFile(const std::string& Ext, const std::string& Title)
{
	return GetSaveFileEx(Ext, Title);
}

std::string	GameScript::GetSaveFileEx(const std::string& Ext, const std::string& Title, const std::string& Dir, const std::string& Reserved)
{
#ifdef _PLATFORM_WIN32
	bool WasFS = IsFullscreenMode();
	if (WasFS)	WindowedMode();
	
	OPENFILENAME ofn;
    memset (&ofn, 0, sizeof (OPENFILENAME));
    
	char szTitle[256] = "Save Level";
	char szInitialDir[256] = ".";
    char szFile[256];
    char szFileTitle[256];
    char szDefExt[32] = "Title";
    char szFilter[128] = "All Files (*.*)\0*.*\0\0";
    szFile[0] = '\0';
    szFileTitle[0] = '\0';

	if (!Ext.empty()) strcpy(szDefExt, Ext.c_str());
	if (!Title.empty()) strcpy(szTitle, Title.c_str());
	if (!Dir.empty()) strcpy(szInitialDir, Dir.c_str());

    ofn.lStructSize = sizeof (OPENFILENAME);
    ofn.lpstrDefExt = szDefExt;
    ofn.lpstrFilter = szFilter;
    ofn.lpstrFile = szFile;
	ofn.lpstrInitialDir = szInitialDir;
    ofn.nMaxFile = 256;
    ofn.lpstrFileTitle = szFileTitle;
    ofn.nMaxFileTitle = 256;
	ofn.hwndOwner    = NULL;
    ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST| OFN_NOCHANGEDIR| OFN_EXPLORER;
    ofn.lpstrTitle = szTitle;

    if (!::GetSaveFileName (&ofn))
	{
		if (WasFS)	FullScreenMode();
        return "";
	}
	

	if (WasFS)	FullScreenMode();
	return ofn.lpstrFile;
#else
	return "";
#endif // _PLATFORM_WIN32
}


std::string	GameScript::GetOpenFile(const std::string& Ext, const std::string& Title)
{
	return GetOpenFileEx(Ext, Title);
}

std::string	GameScript::GetOpenFileEx(const std::string& Ext, const std::string& Title, const std::string& Dir, const std::string& Reserved)
{
#ifdef _PLATFORM_WIN32
	bool WasFS = IsFullscreenMode();
	if (WasFS)	WindowedMode();

	OPENFILENAME ofn;
    memset (&ofn, 0, sizeof (OPENFILENAME));
    
	char szTitle[256] = "Load Level";
	char szInitialDir[256] = ".";
    char szFile[256];
    char szFileTitle[256];
    char szDefExt[32] = "Title";
    char szFilter[128] = "All Files (*.*)\0*.*\0\0";
//	sprintf(szFilter, "%s (*.lvl)\0*.%s\0All Files (*.*)\0*.*\0\0", Title.c_str(), Ext.c_str());
    szFile[0] = '\0';
    szFileTitle[0] = '\0';

	if (!Ext.empty()) strcpy(szDefExt, Ext.c_str());
	if (!Title.empty()) strcpy(szTitle, Title.c_str());
	if (!Dir.empty()) strcpy(szInitialDir, Dir.c_str());

    ofn.lStructSize = sizeof (OPENFILENAME);
    ofn.lpstrDefExt = szDefExt;
    ofn.lpstrFilter = szFilter;
	ofn.lpstrInitialDir = szInitialDir;
    ofn.lpstrFile = szFile;
    ofn.nMaxFile = 256;
    ofn.lpstrFileTitle = szFileTitle;
    ofn.nMaxFileTitle = 256;
	ofn.hwndOwner       = NULL;
    ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_EXPLORER;
	ofn.lpstrTitle = szTitle;
	
    if (!::GetOpenFileName (&ofn))
	{
//		GET_MOUSE().SetMouseVisible(false);
		if (WasFS)	FullScreenMode();
		return "";
	}
//	GET_MOUSE().SetMouseVisible(false);

	// fixme: find a way to make path local?
	if (WasFS)	FullScreenMode();
	
	return ofn.lpstrFile;
#else
	return "";
#endif // _PLATFORM_WIN32
}

static	bool LoadLanguage(const std::string& File)
{
	int Index = (int)File.find_last_of("/\\.");
	bool bRet;

	if (Index >= 0)
	{
		// is file name
		bRet = lang::StrMan::Instance().LoadLangBin(File);
	}
	else
	{
		// is language name
		bRet = lang::StrMan::Instance().LoadLangBin( g_pApp->GetLocalizationFile(File) );
	}

	if (bRet)
		ui::UIManager::Instance().RefreshText();

	return bRet;
}

static std::string LoadDefaultLanguage()
{
	std::string DefLang = g_pApp->GetDefaultLocalization();
	lang::StrMan::Instance().LoadLangBin( g_pApp->GetLocalizationFile(DefLang) );
	ui::UIManager::Instance().RefreshText();
	return DefLang;
}

static void	StartPlayTestSound()
{
	/*const float MIN_UPDATE_STEP = 0.8f;
	static float LastUpdate = 0;
	static unsigned long SoundID = NULL;
	static unsigned long TestSound = NULL;
	float	Time = TIMER().GetTimeStamp();
	if (Time-LastUpdate < MIN_UPDATE_STEP) return;
	LastUpdate = Time;

	if (!SoundID)
	{
		SoundID = AUDIO().LoadSound("sounds/block_select.ogg");
	}
	if (SoundID)
	{
		if (!AUDIO().IsPlaying(SoundID))
			TestSound = AUDIO().PlaySFX(SoundID);
	}*/
}

void	GameScript::StripFilePath(std::string& Filename)
{
	int Index = (int)Filename.find_last_of("/\\");
	if (Index >= 0)
	{
		std::string	NameOnly =  Filename.substr(Index+1, Filename.size()-Index);
		Filename = Filename.substr(0, Index);
		Index =(int) Filename.find_last_of("/\\");
		if (Index >= 0)
		{
			Filename = Filename.substr(Index+1, Filename.size()-Index) + "/" + NameOnly;
		} else
		{
			Filename = Filename + "/" + NameOnly;
		}
	}
	
}

std::string		GameScript::GetFileNameOnly(const std::string& FilenameWithPath, bool WithExtension)
{
	int Index = (int)FilenameWithPath.find_last_of("/\\");
	if (Index >= 0)
	{
		std::string	NameOnly =  FilenameWithPath.substr(Index+1, FilenameWithPath.size()-Index);
		Index = (int)NameOnly.find_last_of(".");
		if (Index >= 0)
		{
			NameOnly = NameOnly.substr( 0, Index);
			return NameOnly;
		}
	}

	return FilenameWithPath;
}

static void	ForceRedraw()
{
	//! USE WITH CARE.. this one doesn't care WHERE you are, it will force the screen to redraw!
	g_pApp->Tick();
}

static void	ConvertAnimations()
{
	bones::BipedAnimationSet::Instance().ConvertAll();
}

void GameScript::Register(LuaPlus::LuaState* _pScript)
{
#ifdef _ENABLE_LUABIND
	
	using namespace luabind;
		
    lua_State * L = _pScript->GetCState ( );

	module(L)
    [

    class_<GameScript>( "Script" )
		.def( "GetOpenFile",	&GameScript::GetOpenFile)
		.def( "GetSaveFile",	&GameScript::GetSaveFile)
		.def( "GetOpenFileEx",	&GameScript::GetOpenFileEx)
		.def( "GetSaveFileEx",	&GameScript::GetSaveFileEx)
	];

	
    // Access file from config	
	luabind::module(L)
    [	 luabind::def("GetScript",     &GameScript::Instance)		];
	

// register the math libs
	luabind::module(L)
    [
			luabind::class_<math::Vec2>( "Vec2" )
			// constructor
			.def(constructor<>())
			.def(constructor<float, float>())
		
			// properties
			.property("x", &math::Vec2::GetX, &math::Vec2::SetX)
			.property("y", &math::Vec2::GetY, &math::Vec2::SetY)

			// operators
			.def( self + self)
			.def( self - self)
			.def(self / float())
			.def(self * float())	
	 

			// functions
			.def( "Normalize",	&math::Vec2::Normalize,		copy(result)	)
			.def( "Magnitude",	&math::Vec2::Magnitude)
			.def( "MagSqr",		&math::Vec2::MagSqr)
	];

	// vec3
	luabind::module(L)
    [
			luabind::class_<math::Vec3>( "Vec3" )
			// constructor
			.def(constructor<>())
			.def(constructor<float, float, float>())
		
			// properties
			.property("x", &math::Vec3::GetX, &math::Vec3::SetX)
			.property("y", &math::Vec3::GetY, &math::Vec3::SetY)
			.property("z", &math::Vec3::GetZ, &math::Vec3::SetZ)

			// functions
			.def( "Normalize",	&math::Vec3::Normalize,		copy(result)	)
			.def( "Magnitude",	&math::Vec3::Magnitude)
			.def( "MagSqr",		&math::Vec3::MagSqr)
	];

	// vec4
	luabind::module(L)
    [
			luabind::class_<math::Vec4>( "Vec4" )
			// constructor
			.def(constructor<>())
			.def(constructor<float, float, float, float>())
	];

	luabind::module(L)
    [
			luabind::class_<math::Ramp>( "FloatRamp" )
			.def( "AddValue",	&math::Ramp::AddValue)
			.def( "GetValue",	&math::Ramp::GetValue)
			.def( "Recalc",		&math::Ramp::Recalc)
	];
	
	luabind::module(L)
    [
	
	 luabind::def("LoadLanguage",			&LoadLanguage			),
	 luabind::def("LoadDefaultLanguage",	&LoadDefaultLanguage	),
	 luabind::def( "HardQuitGame",			&HardQuitGame			),
	 luabind::def( "FullScreenMode",		&FullScreenMode			),
	 luabind::def( "WindowedMode",			&WindowedMode			),
	 luabind::def( "IsFullscreenMode",		&IsFullscreenMode		),
	 luabind::def( "CreateFont",			&CreateNewFont			),
	 luabind::def( "ResizeFont",			&ResizeExistingFont 	),
	 luabind::def( "SetFontBorder",			&SetFontBorder			),
	 luabind::def( "SetFontColorMask",		&SetFontColorMask		),
	 luabind::def( "TakeScreenshot",		&TakeScreenshot			),
	 luabind::def( "ToggleFilter",			&ToggleFilter			),
	 luabind::def( "ReloadImages",			&ReloadImages			),
	 luabind::def( "ForceSave",				&ForceSave				),
	 luabind::def( "StartPlayTestSound",	&StartPlayTestSound 	),
	 luabind::def( "ForceRedraw",			&ForceRedraw			),
	 luabind::def( "SetLocale",				&SetLocale				),
	 luabind::def( "ResetFonts",			&ResetFonts				),
	 luabind::def( "FormatCommaNumber",		&FormatCommaNumber		),
	 luabind::def( "ConvertAnimations",		&ConvertAnimations		)
	 
	];


	if (g_pApp)	g_pApp->GetRMS()->Register(_pScript);
	script::FileList::Register(_pScript);
	ui::UIManager::Register(_pScript);
	
	audio::Audio::Register(_pScript);

	gameflow::GameFlowManager::Register(_pScript);
	vfs::Archive::Register(_pScript);

	//	web::Server::Register(_pScript);
#endif //#_ENABLE_LUABIND

}
